/**
 * 2017年11月14日
 */
package exp.algorithm.sic.insts;

import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import exp.algorithm.sic.InstanceFeatureVectors;
import exp.algorithm.sic.feature.FeatureVector;
import exp.util.DatasetsUtil;
import exp.util.MatrixUtil;
import weka.clusterers.SimpleKMeans;
import weka.core.Attribute;
import weka.core.DenseInstance;
import weka.core.Instance;
import weka.core.Instances;

/**
 * @author Alex
 *
 */
public class KMeansCentroidConverter implements FeatureVector2InstanceConverter{
	private static final long serialVersionUID = 1L;
	Logger log = LoggerFactory.getLogger(KMeansCentroidConverter.class);
	SimpleKMeans skm;
	int featureGroup = 15;
	//最终最终转换成的实例集
	Instances finalInsts = null;
	//用于kmeans的实例集
	Instances featureInsts = null;
	int kMeansInstsAttributeNum = -1;
	int finalInstsAttributeNum = -1;

	public KMeansCentroidConverter() {
		featureGroup = 15;
	}
	
	public KMeansCentroidConverter(int k) {
		this.featureGroup = k;
	}
	
	//形成新的featureinsts结构,构造kmeans模型
	@Override
	public void initialize(List<InstanceFeatureVectors> vecs,Instances original) {
		int num = vecs.size();
		kMeansInstsAttributeNum = vecs.get(0).vectorLength;
		Instances insts = DatasetsUtil.initInstancesWithParmas("kMeansUsedInstances", kMeansInstsAttributeNum, "local_vec_", num, false, null);
		this.featureInsts = insts;
		//形成kmeans所用的数据
		for(InstanceFeatureVectors vec:vecs){
			for(FeatureVector fv:vec.vecs){
				Instance in = new DenseInstance(insts.numAttributes());
				in.setDataset(insts);
				for(int i =0;i<fv.length();i++){
					in.setValue(i, fv.getVal(i));
				}
				insts.add(in);
			}
		}
		skm = new SimpleKMeans();
		try{
			skm.setNumClusters(featureGroup);
			skm.buildClusterer(insts);
			
		}catch(Exception e){
			log.error("Exception when training kmeans : {} " ,e.getMessage());
		}
	}

	@Override
	public Instances converteInstances(List<InstanceFeatureVectors> instancesVectors,Instances original) {
		int totalFeature =  instancesVectors.stream().map(x->x.vecs.size()).reduce(0, (x,y)->x>y?x:y) * kMeansInstsAttributeNum;
		this.finalInstsAttributeNum = totalFeature ;
		
		Instances finalInstances = DatasetsUtil.initInstancesWithParmas("fianlInstances", finalInstsAttributeNum, "f_att_", instancesVectors.size(), true,(Attribute)original.classAttribute().copy());
		this.finalInsts = finalInstances;
		for(InstanceFeatureVectors ifv:instancesVectors){
			Instance oneInst = instanceVector2Instance(ifv, this.featureInsts,finalInstances);
			oneInst.setClassValue(ifv.classValue);
			finalInstances.add(oneInst);
		}
		return finalInstances;
	}

	@Override
	public Instance converteInstance(InstanceFeatureVectors input) {
		return instanceVector2Instance(input, this.featureInsts, finalInsts);
	}
	//to final instance
	private Instance instanceVector2Instance(InstanceFeatureVectors isa,Instances featuresInsts,Instances finalHeader){
		//int beginIdx = 0;
		//这个里面存放的是聚类的id
		int numAtt = finalHeader.numAttributes();
		double finalAtts[] = new double[numAtt];
		List<FeatureVector> fvs = isa.vecs;
		try {
			for(int i=0;i<fvs.size() && i<numAtt-1;){
				FeatureVector fv = fvs.get(i);
				int cid = getKMeansClusterId(fv.toFloatArray(),featuresInsts);
				double [] centroid = skm.getClusterCentroids().instance(cid).toDoubleArray();
				System.arraycopy(centroid, 0, finalAtts, i, centroid.length);
//				finalAtts[i] = getKMeansClusterId(fv.toFloatArray(),featuresInsts);
				
				i+=kMeansInstsAttributeNum;
			}
		} catch (Exception e) {
			log.info("Exception  {} ",e.getMessage());
		}
		//设定类标
		finalAtts[finalAtts.length-1] = isa.classValue;
		Instance in = new DenseInstance(1.0,finalAtts);
		in.setDataset(finalHeader);
		return in;
	}
	private int getKMeansClusterId(float[]arr,Instances ds) throws Exception{
		Instance inst = new DenseInstance(1.0,MatrixUtil.floatArrayToDoubleArray(arr));
		inst.setDataset(ds);
		int clusterId = skm.clusterInstance(inst);
		return clusterId;
	}

	@Override
	public Instances getTransformedInstancesStructure() {
		return finalInsts;
	}
}
